#
#   EvilGreg.py
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; version 2 of the License.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   Copyright
#   Author: Nicholas F. Hoover
#   Contributors: Salvatore S. Gionfriddo
#   Created: 2007.06.18
#   Last Modified: 2007.07.11
#

import pygame
from GameObject import GameObject
from Character import Character
from Animation import Animation

class EvilGreg(Character):

#########
# STATS #
#########
    MIN_HEALTH = 0
    MAX_HEALTH = 100

    MAX_HUNGER = 100
    MIN_HUNGER = 0
    START_HUNGER = 40

    STATUS_TICKS_BETWEEN_UPDATES = 20
    HUNGER_DAMAGE_PER_TICK = 2
    REGEN_AMOUNT_PER_TICK = 1

    STARVATION_DAMAGE = 4

    STARVATION_MIN = 0
    STARVATION_MAX = 20
    
    HEARTBURN_MIN  = 80
    HEARTBURN_END  = 70

##############
# ANIMATIONS #
##############

    NUM_ANIMATIONS = 8
    (IDLE,
     WALK_UP,
     WALK_DOWN,
     WALK_SIDE,
     PUNCH,
     KICK,
     EAT,
     FLAME) = range(NUM_ANIMATIONS)


    LEFT = NUM_ANIMATIONS

    ###############
    # FPS FACTORS # FPS/FACTOR = ticks between updates
    ###############
    IDLE_FPS_FACTOR  = 8  #4
    WALK_FPS_FACTOR  = 8  #4
    PUNCH_FPS_FACTOR = 30 #1 * 3 frames = 3  updates for 2 kills .66 efficiency
    KICK_FPS_FACTOR  = 15 #2 * 5 frames = 10 updates for 7 kills .7 efficiency
    EAT_FPS_FACTOR   = 30 #1
    FLAME_FPS_FACTOR = 15 #2

    ##############
    # KEY FRAMES #
    ##############
    PUNCH_KEY_FRAME = 1
    KICK_KEY_FRAME = 2
    EAT_KEY_FRAME = 3
    FLAME_KEY_FRAME = 3

    #########################
    # ATTACK COLLISION DATA #
    #########################
    PUNCH_OFFSET = (10,50)
    PUNCH_SIZE   = (20,10)
    KICK_OFFSET  = (15,60)
    KICK_SIZE    = (15,20)
    EAT_OFFSET   = (10,80)
    EAT_SIZE     = (12,12)
    
    ###################
    # ANIMATION TABLE #
    ###################
    ANIMATION_TABLE = [
        #FACING RIGHT
        (IDLE,      'EG_Idle_Right',     IDLE_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (WALK_UP,   'EG_WalkSide_Right', WALK_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (WALK_DOWN, 'EG_WalkSide_Right', WALK_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (WALK_SIDE, 'EG_WalkSide_Right', WALK_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (PUNCH,     'EG_Punch_Right',    PUNCH_FPS_FACTOR, PUNCH_KEY_FRAME),
        (KICK,      'EG_Kick_Right',     KICK_FPS_FACTOR,  KICK_KEY_FRAME),
        (EAT,       'EG_Eat_Right',      EAT_FPS_FACTOR,   EAT_KEY_FRAME),
        (FLAME,     'EG_Flame_Right',    FLAME_FPS_FACTOR, FLAME_KEY_FRAME),
        
        #FACING LEFT
        (IDLE      + LEFT, 'EG_Idle_Left',     IDLE_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (WALK_UP   + LEFT, 'EG_WalkSide_Left', WALK_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (WALK_DOWN + LEFT, 'EG_WalkSide_Left', WALK_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (WALK_SIDE + LEFT, 'EG_WalkSide_Left', WALK_FPS_FACTOR,  Animation.NO_KEY_FRAME),
        (PUNCH     + LEFT, 'EG_Punch_Left',    PUNCH_FPS_FACTOR, PUNCH_KEY_FRAME),
        (KICK      + LEFT, 'EG_Kick_Left',     KICK_FPS_FACTOR,  KICK_KEY_FRAME),
        (EAT       + LEFT, 'EG_Eat_Left',      EAT_FPS_FACTOR,   EAT_KEY_FRAME),
        (FLAME     + LEFT, 'EG_Flame_Left',    FLAME_FPS_FACTOR, FLAME_KEY_FRAME),
        ]

    #############
    # MOVEMENTS #
    #############
    MOVEMENTS = [(0,0),   #IDLE
                 (0,-20), #WALK_UP
                 (0,20),  #WALK_DOWN
                 (20,0),  #WALK_SIDE
                 (0,0),   #PUNCH
                 (0,0),   #KICK
                 (0,0),   #EAT
                 (0,0),   #FLAME
                 ]

    DEFAULT_ANIMATION = IDLE

##########
# SOUNDS #
##########
    NUM_SOUNDS = NUM_ANIMATIONS

    ###############
    # SOUND TABLE #
    ###############
    SOUND_TABLE = [GameObject.BLANK_SOUND_ENTRY, #IDLE
                   GameObject.BLANK_SOUND_ENTRY, #RUN_UP
                   GameObject.BLANK_SOUND_ENTRY, #RUN_DOWN
                   GameObject.BLANK_SOUND_ENTRY, #RUN_SIDE
                   (PUNCH, 'EG_Punch'),          #PUNCH
                   (KICK,  'EG_Kick'),           #KICK
                   (EAT,   'EG_Eat'),            #EAT
                   (FLAME, None)] #,'EG_Flame')]          #FLAME

###########
# ATTACKS #    
###########
    PUNCH_DAMAGE = 200  #Kill 2 kids
    KICK_DAMAGE  = 700  #Kill 7 kids
    FLAME_DAMAGE = 2000 #Kill 20 kids
    FLAME_COST = 10

########
# MISC #
########
    MAX_POWERUPS_EAT = 3
    DEFAULT_POSITION = (300,300)
    RECT_SIZE = (60,100)


#############
# FUNCTIONS #
#############
    def __init__(self):
        GameObject.__init__(self)
        self.animations = []
        self.reset()

    def reset(self):
        self.currentAnimation = EvilGreg.DEFAULT_ANIMATION
        self.facing = GameObject.RIGHT
        self.rect.topleft = EvilGreg.DEFAULT_POSITION
        if(self.animations): self.updateCurrentImage() #Hack makes sure image is updated after a death
        
        #status
        self.health = self.MAX_HEALTH
        self.hunger = self.START_HUNGER
        self.kills = 0
        self.kidsEaten = 0
        self.killsByFlame = 0
        self.killsByPunch = 0
        self.killsByKick = 0
        
        self.heartBurn = False
        
        self.statusUpdateCount = 0 #hunger/regen
        
        self.effects = []
        
        self.nextAction = EvilGreg.IDLE
        self.nextFacing = self.facing

###########
# Updates #
###########
    def update(self):
        if self.animations[self.currentAnimation].update():
            for action in [EvilGreg.PUNCH, EvilGreg.KICK, EvilGreg.EAT, EvilGreg.FLAME]:
                self.checkForStopAction(action)

            #update the image to be displayed
            GameObject.updateCurrentImage(self)

            #I like to move it, move it. Move it, move it.
            #HACK
            movement = self.MOVEMENTS[self.currentAnimation - self.facing]
            if self.currentAnimation == EvilGreg.WALK_SIDE + EvilGreg.LEFT:
                movement = (-movement[0], movement[1])
            if self.rect.right + movement[0] <= 825 and self.rect.left + movement[0] >= -25: # mad hax
                self.rect.left += movement[0]
            if self.rect.bottom + movement[1] <= 625 and self.rect.top + movement[1] >= -25: # mad hax
                self.rect.top += movement[1]

            self.doStatusUpdate()
            
    def doStatusUpdate(self):
        self.statusUpdateCount += 1
        if self.statusUpdateCount == self.STATUS_TICKS_BETWEEN_UPDATES:
            self.statusUpdateCount = 0
            self.doRegen()
            self.doHunger()
            self.doEffects()

    def doRegen(self):
        self.incHealth(self.REGEN_AMOUNT_PER_TICK)
 
    def doHunger(self):
        self.decHunger(self.HUNGER_DAMAGE_PER_TICK)
        if self.isStarving():
            self.decHealth(self.STARVATION_DAMAGE)
        if self.hunger >= self.HEARTBURN_MIN:
            self.heartBurn = True
        elif self.hunger < self.HEARTBURN_END:
            self.heartBurn = False
    
    def addEffect(self, effect):
        #apply instant effect
        if effect.i_change:
            EvilGreg.__dict__[effect.i_change](self, effect.i_change_amount)
        #add to effect list
        self.effects.append(effect)
        
    def doEffects(self):
        for effect in self.effects[:]:
            #check effect age
            if effect.age >= effect.life:
                #apply end effect
                if effect.e_change:
                    EvilGreg.__dict__[effect.e_change](self, effect.e_change_amount)
                #remove from effect list
                self.effects.remove(effect)
            else:
                #increment effect age
                effect.age += 1
                #apply durational effect
                if effect.change:
                    EvilGreg.__dict__[effect.change](self, effect.change_amount)

#########
# STATS #
#########
    def incHunger(self,amount):
        self.hunger += amount
        if self.hunger > self.MAX_HUNGER:
            self.hunger = self.MAX_HUNGER

    def decHunger(self,amount):
        self.hunger -= amount
        if self.hunger < self.MIN_HUNGER: self.hunger = self.MIN_HUNGER
    
    def incHealth(self, amount):
        self.health += amount
        if self.health > self.MAX_HEALTH: self.health = self.MAX_HEALTH
    def decHealth(self, amount):
        self.health -= amount
        if self.health < self.MIN_HEALTH: self.health = self.MIN_HEALTH

    def setHeartBurn(self, status):
        self.heartBurn = status

    def getKills(self):
        return self.kills
    def getKidsEaten(self):
        return self.kidsEaten
    def getKillsByFlame(self):
        return self.killsByFlame
    def getKillsByKick(self):
        return self.killsByKick
    def getKillsByPunch(self):
        return self.killsByPunch
    def resetKills(self):
        self.kills = 0
        self.kidsEaten = 0
        self.killsByPunch = 0
        self.killsByKick = 0
        self.killsByFlame = 0

    def getStats(self):
        return (self.health, self.hunger, self.heartBurn, self.kills)
    
    def isStarving(self):
        return self.hunger >= self.STARVATION_MIN and self.hunger <= self.STARVATION_MAX

    def isSatiated(self):
        return self.hunger == self.MAX_HUNGER



###########
# ACTIONS #
###########

    def walk(self,direction, facing = None):
        if self.canAct():
            if facing != None: self.setFacing(facing)
            self.currentAnimation = direction + self.facing

        if facing != None: self.nextFacing = facing
        else: self.nextFacing = self.facing
        self.nextAction = direction

    def stopWalk(self, direction, facing = None):
        self.nextAction = EvilGreg.IDLE
        self.nextFacing = self.facing
        if facing == None: facing = self.facing
        if self.currentAnimation == direction + facing:
            self.startIdling()

    def punch(self):
        if self.canAct():
            self.currentAnimation = self.PUNCH + self.facing
            self.updateCurrentImage()
            self.resetAnimationFrame()
            self.sounds[self.PUNCH].play()

    def kick(self):
        if self.canAct():
            self.currentAnimation = self.KICK + self.facing
            self.updateCurrentImage()
            self.resetAnimationFrame()
            self.sounds[self.KICK].play()
      
    def eat(self):
        if self.canAct():
            self.currentAnimation = self.EAT + self.facing
            self.updateCurrentImage()
            self.resetAnimationFrame()


    def flame(self):
        if self.canAct() and self.heartBurn:
            self.currentAnimation = EvilGreg.FLAME + self.facing
            self.decHunger(EvilGreg.FLAME_COST)
            self.updateCurrentImage()
            self.resetAnimationFrame()


    def checkForStopAction(self, action):
        if self.isActionComplete(action):
            self.doNextAction()
            
    def doNextAction(self):
        self.facing = self.nextFacing
        self.currentAnimation = self.nextAction + self.facing
        self.updateCurrentImage()
        self.resetAnimationFrame()
        
    def clearNextAction(self):
        self.nextAction = EvilGreg.IDLE
        self.nextFacing = self.facing
        self.doNextAction()

    def startIdling(self):
        self.currentAnimation = self.IDLE + self.facing
        
    def canAct(self):
        return self.currentAnimation - self.facing <= self.WALK_SIDE
        
    def createPunchSprite(self):
        punchSprite = pygame.sprite.Sprite()
        if self.facing == EvilGreg.RIGHT:
            punchSprite.rect = pygame.Rect((self.rect.centerx + EvilGreg.PUNCH_OFFSET[0], self.rect.top + EvilGreg.PUNCH_OFFSET[1]), EvilGreg.PUNCH_SIZE)
        else:
            punchSprite.rect = pygame.Rect((self.rect.centerx - EvilGreg.PUNCH_OFFSET[0] - EvilGreg.PUNCH_SIZE[0], self.rect.top + EvilGreg.PUNCH_OFFSET[1]), EvilGreg.PUNCH_SIZE)
        return punchSprite

    def createKickSprite(self):
        kickSprite = pygame.sprite.Sprite()
        if self.facing == EvilGreg.RIGHT:
            kickSprite.rect = pygame.Rect((self.rect.centerx + EvilGreg.KICK_OFFSET[0], self.rect.top + EvilGreg.KICK_OFFSET[1]), EvilGreg.KICK_SIZE)
        else:
            kickSprite.rect = pygame.Rect((self.rect.centerx - EvilGreg.KICK_OFFSET[0] - EvilGreg.KICK_SIZE[0], self.rect.top + EvilGreg.KICK_OFFSET[1]), EvilGreg.KICK_SIZE)
        return kickSprite

    def createEatSprite(self):
        eatSprite = pygame.sprite.Sprite()
        if self.facing == EvilGreg.RIGHT:
            eatSprite.rect = pygame.Rect((self.rect.centerx + EvilGreg.EAT_OFFSET[0], self.rect.top + EvilGreg.EAT_OFFSET[1]), EvilGreg.EAT_SIZE)
        else:
            eatSprite.rect = pygame.Rect((self.rect.centerx - EvilGreg.EAT_OFFSET[0] - EvilGreg.EAT_SIZE[0], self.rect.top + EvilGreg.EAT_OFFSET[1]), EvilGreg.EAT_SIZE)
        return eatSprite


class DeadEvilGreg(GameObject):
    
    NUM_ANIMATIONS = 1
    DEAD = 0


    LEFT = NUM_ANIMATIONS

    ###################
    # ANIMATION TABLE #
    ###################
    ANIMATION_TABLE = [
        (DEAD,        'EG_Dead_Right', 1, Animation.NO_KEY_FRAME),
        (DEAD + LEFT, 'EG_Dead_Left',  1, Animation.NO_KEY_FRAME),
        ]

    #############
    # MOVEMENTS #
    #############
    MOVEMENTS = [(0,0),   #DEAD
                 ]

    DEFAULT_ANIMATION = DEAD

##########
# SOUNDS #
##########
    NUM_SOUNDS = NUM_ANIMATIONS

    ###############
    # SOUND TABLE #
    ###############
    SOUND_TABLE = [GameObject.BLANK_SOUND_ENTRY, #DEAD
                   ]

    RECT_SIZE = (100, 60)

    def __init__(self):
        GameObject.__init__(self)
        
    def update(self):
        self.animations[self.currentAnimation].update()
        self.updateCurrentImage()



